#! /usr/bin/perl
use strict;

my $copyright = <<EOT;
// Copyright (c) 2009 Helmar Wodtke. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// The MIT License is an OSI approved license and can
// be found at
//   http://www.opensource.org/licenses/mit-license.php

EOT

while (<DATA>) {
  last if /^%%\s*$/;
  s/%copyright%/$copyright/;
  print $_;
}

sub func {
  my $n = shift;
  return "" if $n == 0;
  return "    a := t.St.Pop(); _ = a;\n"
    if $n == 1;
  return "    a := t.St.Drop($n); _ = a;\n"
}
my %funcs;
my $func;
while (<DATA>) {
  last if /^%%\s*$/;
  if (/^(\S+)\s+(\S+)\s+(\S+)\s*$/) {
    my ($st, $fn, $fl) = ($1, $2, $3);
    if ($fl eq "+") {
      $funcs{"begin$fn"} = func 0;
      $funcs{"begin$fn"} .= "    t.Args = t.St.Drop($st);\n";
      $funcs{"begin$fn"} .= "    t.Marker = t.St.Depth();\n";
      $fn = "end$fn";
      $st = 0;
    }
    $func = $fn;
    $funcs{$func} = func $st;
    if ($fl eq "+") {
      $funcs{$func} .= "    a := t.St.Drop(t.St.Depth() - t.Marker); _ = a;\n";
    }
    next;
  }
  if (/^\s+[+](\s+.*)$/) {
    my $n = $1;
    $funcs{$func} =~ s/ _ = a;\n$/\n/s;
    $funcs{$func} .= "$n\n";
    next;
  }
}
my @funcs;
for (sort keys %funcs) {
  push @funcs, "  \"$_\" : func (t *CharMapperI) {\n$funcs{$_}  }";
}
print "var Ops = map[string]func (t *CharMapperI) {\n";
print join(",\n", @funcs);
print ",\n}\n\n";

while (<DATA>) {
  last if /^%%\s*$/;
  print $_;
}

__DATA__
%copyright%
package cmapi

// CMap "interpreter" - this PS btw.
// WARNING: This file is automatically generated!
//          It makes no sense to change anything here.

import (
  "ps";
  "util";
  "cmapt";
  "fancy";
  "xchar";
)

type CharMapperT struct {
  Ranges, Uni *cmapt.CMapT;
}

func New() *CharMapperT {
  r := new(CharMapperT);
  r.Ranges = cmapt.New();
  r.Uni = cmapt.New();
  return r;
}

type CharMapperI struct {
  Target *CharMapperT;
  St stacks.Stack;
  Dic map[string][]byte;
  Marker int;
  Args [][]byte;
}


func NewInterpreter(t *CharMapperT) *CharMapperI {
  r := new(CharMapperI);
  r.Target = t;
  r.St = stacks.NewStack(1024);
  r.Dic = make(map[string][]byte);
  return r;
}

%%
1 dict           -
  +    t.St.Push([]byte{'?'});
  +    _ = a;
1 begin          -
0 end            -
1 dup            -
  +    t.St.Push(a);
  +    t.St.Push(a);
1 pop            -
2 exch           -
  +    t.St.Push(a[1]);
  +    t.St.Push(a[0]);
2 def            -
  +    t.Dic[string(a[0])] = a[1];
0 currentdict    -
  +    t.St.Push([]byte{'?'});
3 defineresource -
  +    t.St.Push([]byte{'?'});
  +    _ = a;
2 findresource   -
  +    t.St.Push([]byte{'?'});
  +    _ = a;

1 usecmap        -
1 usefont        -
1 usematrix      +
1 codespacerange +
  +    for k := 0; k < len(a); k += 2 {
  +      to, l := ps.StrIntL(ps.String(a[k + 1]));
  +      t.Target.Ranges.AddDef(int(a[k][0]), int(a[k+1][0]) + 1, l);
  +      t.Target.Ranges.AddDef(ps.StrInt(ps.String(a[k])), to + 1, l); // just not used.
  +    }
0 begincmap      -
0 endcmap        -
1 bfchar         +
  +    for k := 0; k < len(a); k += 2 {
  +      t.Target.Uni.Add(ps.StrInt(ps.String(a[k])), ps.StrInt(ps.String(a[k+1])));
  +    }
1 bfrange        +
  +    for k := 0; k < len(a); k += 3 {
  +      // leaving the array expression as it is: invalidate - we do not have char names to unicode now
  +      t.Target.Uni.AddRange(ps.StrInt(ps.String(a[k])),
 +         ps.StrInt(ps.String(a[k+1])), ps.StrInt(ps.String(a[k+2])));
  +    }
1 cidchar        +
1 cidrange       +
1 notdefchar     +
1 notdefrange    +
2 rearrangedfont +
%%

func Read(rdr fancy.Reader) (r *CharMapperT) {
  r = New();
  if rdr == nil { // make identity setup
    r.Uni.AddRange(0, 256, 0);
    r.Ranges.AddDef(0, 256, 1);
    return;
  }
  cm := NewInterpreter(r);
  for {
    t, _ := ps.Token(rdr);
    if len(t) == 0 {
      break
    }
    if f, ok := Ops[string(t)]; ok {
      f(cm)
    } else {
      cm.St.Push(t)
    }
  }
  return;
}

func Decode(s []byte, to *CharMapperT) (r []byte) {
  r = make([]byte, len(s)*6);
  p := 0;
  for k := 0; k < len(s); {
    l := to.Ranges.Code(int(s[k]));
    a := ps.StrInt(s[k : k+l]);
    k += l;
    p += xchar.EncodeRune(to.Uni.Code(a), r[p:len(r)]);
  }
  return r[0:p];
}
